import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_test1/bean/BaseBean.dart';
import 'package:flutter_test1/constant/Constant.dart';
import 'package:flutter_test1/preference/PreferencesManager.dart';
import 'package:flutter_test1/views/login/login.dart';

typedef JsonConverter<T> = T Function(Map<String, dynamic> json);

class YYRequestManager {
  String baseUrl;
  Map<String, String>? baseHeaders;
  late Dio dio;
  late Dio downloadDio;

  YYRequestManager({
    required this.baseUrl,
    this.baseHeaders,
  }) {
    dio = new Dio();
    BaseOptions baseOptions = BaseOptions(headers: baseHeaders);
    baseOptions.baseUrl = baseUrl;
    baseOptions.connectTimeout = 15000;
    dio.options = baseOptions;
    dio.interceptors.add(InterceptorsWrapper(onRequest: (
      RequestOptions options,
      RequestInterceptorHandler handler,
    ) {
      String token = PreferencesManager.getInstance().getString(Constants.TOKEN);
      options.headers["token"] = token;
      handler.next(options);
    }, onResponse: (
      Response e,
      ResponseInterceptorHandler handler,
    ) async {
      BaseBean b = BaseBean.fromJson(e.data);
      if (b.code == 402) {
        handler.reject(DioError(
            requestOptions: e.requestOptions,
            response: e,
            error: TokenException()));
        dio.clear();
      } else {
        handler.next(e);
      }
      // handler.next(e);
    }));
  }

  Future<YYResultModel<T>> requestPost<T>(
    url,
    params,
    JsonConverter<T> jsonConverter, {
    Map<String, String>? headers,
    BuildContext? context,
    noTip = false,
    CancelToken? cancelToken,
  }) async {
    Options option = new Options();
    option.headers = headers;
    option.method = "POST";

    return await requestBase(url, params, headers, option, jsonConverter,
        noTip: noTip, context: context, cancelToken: cancelToken);
  }

  Future<YYResultModel<T>> requestGet<T>(
    url,
    params,
    JsonConverter<T> jsonConverter, {
    Map<String, String>? headers,
    BuildContext? context,
    noTip = false,
    CancelToken? cancelToken,
  }) async {
    Options option = new Options();
    option.method = "GET";
    option.headers = headers;
    return await requestBase(url, params, headers, option, jsonConverter,
        noTip: noTip, context: context, cancelToken: cancelToken);
  }

  Future<YYResultModel<T>> requestBase<T>(
    url,
    params,
    Map<String, String>? headers,
    Options option,
    JsonConverter<T> jsonConverter, {
    BuildContext? context,
    noTip = false,
    int connectTimeout = 15000,
    CancelToken? cancelToken,
  }) async {
    // 判断网络

    // var connectivityResult = await (connectivity.checkConnectivity());
    // if (connectivityResult == ConnectivityResult.mobile) {
    // } else if (connectivityResult == ConnectivityResult.wifi) {
    // } else if (connectivityResult == ConnectivityResult.none) {
    //   return YYResultModel(null, false, YYResultCode.NETWORK_ERROR, "请检查网络");
    // }
    try {
      Response response = await dio.request(
        url,
        queryParameters: params,
        options: option,
      );
      // debug模式打印相关数据
      {
        debugPrint('请求url: ' + url);
        debugPrint('基础请求头: ' + dio.options.headers.toString());
        debugPrint('请求头: ' + option.headers.toString());
        if (params != null) {
          debugPrint('请求参数: ' + params.toString());
        }
        if (response.headers != null) {
          debugPrint('返回头: ' + response.headers.toString());
        }
        debugPrint("data:${response.data}");
      }
      if(EasyLoading.isShow){
        EasyLoading.dismiss();
      }
      BaseBean baseBean = jsonConverter(response.data) as BaseBean;
      if(baseBean.code != YYResultCode.SUCCESS){
        EasyLoading.showToast(baseBean.msg);
      }
      return new YYResultModel<T>(
          jsonConverter(response.data!), baseBean.code, baseBean.msg, time: 0);
    } on DioError catch (error) {
      if(EasyLoading.isShow){
        EasyLoading.dismiss();
      }
      debugPrint('请求url: ' + url);
      if (params != null) {
        debugPrint('请求参数: ' + params.toString());
      }
      debugPrint(
          'error:${error.type},${error.error}|${error.error?.runtimeType ?? ""},${error.response?.statusCode},${error.response?.statusMessage}');
      if (error.response!.statusCode == 401) {//没有权限token过期
        jumpToLogin(context);
      }
      return YYResultModel<T>(null, error.response!.statusCode!.toString(), "服务器异常");
    }
  }
  bool blockingLoginIfTokenException = false;

  void jumpToLogin(BuildContext? context) {
    debugPrint('login jumpToLogin $context');
    if (context != null) {
      if (!blockingLoginIfTokenException) {
        blockingLoginIfTokenException = true;
        logoutInternal(context).then((value) => null).whenComplete(() {
          debugPrint("re login completed");
          blockingLoginIfTokenException = false;
        });
      }
    }
  }

  Future<void> logoutInternal(BuildContext context) async {
    await Future.delayed(Duration(milliseconds: 500));

    var v = await Navigator.pushAndRemoveUntil(
        context, new MaterialPageRoute(builder: (context) => new LoginPage()),
        (route) {
      return false;
    });
  }
}

class YYResultModel<T> {
  T? data;
  String localCode;
  String localMessage;

  int? time;

  YYResultModel(this.data, this.localCode, this.localMessage,
      { this.time});

  bool get success => localCode == YYResultCode.SUCCESS;

  String get message {
    if (!success) {
      return localMessage;
    } else if (data is BaseBean) {
      return (data as BaseBean).msg;
    }
    return "";
  }

  String get code {
    if (!success) {
      return localCode;
    } else if (data is BaseBean) {
      return (data as BaseBean).code;
    }
    return YYResultCode.SUCCESS;
  }

  @override
  String toString() {
    return 'YYResultModel{data: $data, success: $success, localCode: $localCode, localMessage: $localMessage,time: $time}';
  }

  void _toastMsg(String msg) {
    if (msg != null && msg.length > 0) {
      EasyLoading.showToast(msg);
    }
  }

  /**
   * 只有本次请求成功，并且服务器返回了0才返回true
   * @showIfSuccess  true:无论成功与否，都弹出提示信息,false:只有失败时才弹出
   * */
  bool checkSuccessAndToast({
    bool showIfFailed = true,
    bool showIfSuccess = true,
    Map<int, String>? messageMatcher,
  }) {
    if (!success) {
      if (showIfFailed) {
        _toastMsg(localMessage);
      }
      return false;
    } else if (data is BaseBean) {
      BaseBean bb = (data as BaseBean);
      String toastMsg = bb.msg;
      if (messageMatcher != null) {
        String? m = messageMatcher[bb.code];
        if (m != null) {
          toastMsg = m;
        }
      }

      if (bb.code == YYResultCode.SUCCESS) {
        if (showIfSuccess) {
          _toastMsg(toastMsg);
        }
        return true;
      } else {
        if (showIfFailed) {
          _toastMsg(toastMsg);
        }
        return false;
      }
    } else {
      return false;
    }
  }

  bool checkRequestSuccess() {
    //检查本次请求是否成功

    return success &&
        data is BaseBean &&
        (data as BaseBean).code == YYResultCode.SUCCESS;
  }
}

class YYResultCode {
  static const SUCCESS = "1";
}

class TokenException implements IOException {
  @override
  String toString() {
    return "TokenException";
  }
}
